package com.example.sqlSession;

import com.example.pojo.Configuration;
import com.example.pojo.MappedStatement;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * 因为insert、update、delete 实际上都是调用 executor的update方法，因此
 * 该类只实现了 update ,insert,delete 是调用的update 方法
 * 在代理方法中新加判断条件，根究调用方法的返回类型判断
 * 如果返回值类型是 Integer 或 Boolean 会调用update 方法
 * 根究方法的返回类型返回具体的类型：
 * 如果原始方法返回是Boolean 返回 Boolean.valueOf(boolean) 因为update方法需要转化(E) 所以需要包装类
 * 如果原始方法返回是Integer 则直接返回结果
 * if (method.getGenericReturnType().getTypeName().equals(Boolean.class.getName())) {
 * return update(method.getDeclaringClass().getName() + "." + method.getName(), args).equals(1) ? Boolean.valueOf(true) : Boolean.valueOf(false);
 * }
 * if (method.getGenericReturnType().getTypeName().equals(Integer.class.getName())) {
 * return update(method.getDeclaringClass().getName() + "." + method.getName(), args);
 * }
 * <p>
 * <p>
 * 在该类中把代理对象判断处理提取公共方法proxyHandle（）可以同时作用与 jdk代理，cglib代理
 *
 * @author kangshuai
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;


    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object... params) throws Exception {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
        return (List<E>) list;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或者返回结果过多");
        }
    }

    @Override
    public <T> T insert(String statementId, Object... params) throws Exception {
        return update(statementId, params);
    }

    @Override
    public <T> T update(String statementId, Object... params) throws Exception {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        return simpleExecutor.update(configuration, mappedStatement, params);
    }

    @Override
    public <T> T delete(String statementId, Object... params) throws Exception {
        return update(statementId, params);
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) throws Exception {
        return (T) Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底层都还是去执行JDBC代码 //根据不同情况，来调用selectList或者selectOne
                // 准备参数 1：statementId :sql语句的唯一标识：namespace.id= 接口全限定名.方法名
                // 方法名：findAll
                // method.getDeclaringClass().getName() 获取当前方法所在类完全限定名
                // method.getName() 获取当前方法名称
                // 准备参数2：params:args
                // 获取被调用方法的返回值类型
                // 判断是否进行了 泛型类型参数化
                return proxyHandle(method, args);
            }
        });
    }


    @Override
    public <T> T getMapperCglib(Class<?> mapperClass) throws Exception {
        return (T) Enhancer.create(mapperClass, new MethodInterceptor() {
            String s="1";
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return proxyHandle(method, objects);
            }
        });
    }

    private Object proxyHandle(Method method, Object[] args) throws Exception {
        if (method.getGenericReturnType() instanceof ParameterizedType) {
            return selectList(method.getDeclaringClass().getName() + "." + method.getName(), args);
        }
        if (method.getGenericReturnType().getTypeName().equals(Boolean.class.getName())) {
            return update(method.getDeclaringClass().getName() + "." + method.getName(), args).equals(1) ? Boolean.valueOf(true) : Boolean.valueOf(false);
        }
        if (method.getGenericReturnType().getTypeName().equals(Integer.class.getName())) {
            return update(method.getDeclaringClass().getName() + "." + method.getName(), args);
        }
        return selectOne(method.getDeclaringClass().getName() + "." + method.getName(), args);
    }
}
